package Alibaba.AlibabaClient;

import java.io.*;
import java.net.*;

import Alibaba.AlibabaUtil.*;

public class Service {

	static String serverURL = "http://localhost:8080";
	static String localDiskDrive = "";

	static String fixFileName(String fileName) {
		String fixedFileName = fileName.replaceAll("\\w:", "");
		fixedFileName = fixedFileName.replaceAll("\\\\", "/");
		return fixedFileName;
	}

	static void restoreFile(String fileName, String userName, int snapshotNum) throws IOException {
		String fixedFileName = fixFileName(fileName);
		System.out.println("Restoring file " + fileName + " (" + fixedFileName + ")");

		URL url = new URL(serverURL + fixedFileName + (snapshotNum != 0 ? "?snapshot=" + snapshotNum : ""));
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();

		connection.setRequestMethod("GET");
		connection.addRequestProperty("Authorization", userName);
		connection.connect();
		int rc = connection.getResponseCode();
		if (rc == HttpURLConnection.HTTP_OK) {
			File outputFile = new File(fixedFileName);
			if (outputFile.exists() && (!outputFile.canWrite())) {
				System.out.println("Failed: Can't write to " + fileName);
				return;
			}
			FileOutputStream outputStream = new FileOutputStream(outputFile);
			outputStream.write(readInputStream(connection.getInputStream()));
			outputStream.close();
			System.out.println("Successfully restored " + fileName);
		} else if (rc == HttpURLConnection.HTTP_NOT_FOUND) {
			System.out.println("Could restore " + fileName + ". File not found on server.");
			return;
		} else {
			System.out.println("Failed restoring " + fileName);
			return;
		}
	}

	static void backupDir(String dirName, String userName) throws IOException {
		String fixedDirName = fixFileName(dirName);
		File dir = new File(fixedDirName);
		File[] children = dir.listFiles();
		if (children == null) {
			System.out.println("Failed: no such directory " + dirName);
		} else {
			System.out.println("Backupping directory " + dirName + " (" + fixedDirName + ")\n");
			for (int i = 0; i < children.length; i++) {
				if (children[i].isFile()) {
					String filename = fixFileName(fixedDirName + children[i].getName());
					backupFile(filename, userName);
					System.out.println();
				}
			}
			System.out.println("\nSuccesfully backupped " + dirName);
		}
	}

	static void restoreDir(String dirName, String userName, int snapshotNum) throws IOException {
		String fixedDirName = fixFileName(dirName);
		String[] listOfFiles = getListOfFiles(fixedDirName, false, userName, snapshotNum);
		System.out.println("Restoring directory " + dirName + " (" + fixedDirName + ")\n");
		for (int i = 0; i < listOfFiles.length; i++) {
			String filename = listOfFiles[i];
			restoreFile(filename, userName, snapshotNum);
			System.out.println();
		}
		System.out.println("\nSuccesfully restored " + dirName);
	}

	static void backupFile(String fileName, String userName) throws IOException {
		String fixedFileName = fixFileName(fileName);
		File inputFile = new File(fixedFileName);

		System.out.println("Backupping file " + fileName + " (" + fixedFileName + ")");

		// read the file into bytes buffer
		FileInputStream inputStream = new FileInputStream(inputFile);
		byte[] inputFileBuffer = new byte[(int) inputFile.length()];
		int streamSize = inputStream.read(inputFileBuffer);

		if (streamSize < inputFileBuffer.length) {
			throw new IOException("Failed: Could not read the entire file");
		}

		// validate the the file signature

		byte[] shaClient = SHACoder.calcSHA256(inputFileBuffer, inputFileBuffer.length);
		String shaClientBase64 = Base64.encodeBytes(shaClient, Base64.URL_SAFE);
		if (backupSignatue(fileName, userName, shaClientBase64)) {
			System.out.println("Successfully backupped " + fileName + " using its signature");
			return;
		}

		// sends request to the server
		URL url = new URL(serverURL + fixedFileName);//
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("PUT");
		connection.setRequestProperty("Authorization", userName);
		connection.setRequestProperty("Content-Length", Long.toString(inputFile.length()));
		connection.setDoOutput(true);
		connection.connect();

		// Send file data to the server
		OutputStream out = connection.getOutputStream();
		out.write(inputFileBuffer);
		int rc = connection.getResponseCode();
		if (rc == HttpURLConnection.HTTP_OK) {
			System.out.println("Succesfully backupped " + fileName + " using its data");
		} else {
			System.out.println("Failed: Could not send file to server");
		}
	}

	static void listFiles(String dirName, String userName, int snapshotNum) throws IOException {
		String[] filesNames = getListOfFiles(dirName, false, userName, snapshotNum);
		System.out.println("Listing directory " + dirName + " (" + fixFileName(dirName) + ")\n");
		if (filesNames != null) {
			System.out.println("The files of the directory " + dirName + " are:");
			for (String fileName : filesNames) {
				System.out.println(fileName);
			}
			System.out.println("\nSuccesfully listed " + dirName);
		} else {
			System.out.println("Failed: no such directory " + dirName);
		}
	}

	static private String[] getListOfFiles(String directoryName, boolean toCheck, String userName, int snapshotNum) throws IOException {
		directoryName = fixFileName(directoryName);
		URL url = new URL(serverURL + directoryName + (toCheck ? "?check" : (snapshotNum != 0 ? "?snapshot=" + snapshotNum : "")));
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();

		connection.setRequestMethod("GET");
		connection.setRequestProperty("Authorization", userName);
		connection.connect();
		int rc = connection.getResponseCode();
		if (rc == HttpURLConnection.HTTP_OK) {
			String allFiles = new String(readInputStream(connection.getInputStream()));
			return allFiles.split("\n");
		} else {
			return null;
		}
	}

	static void checkFiles(String dirName, String userName) throws IOException {
		String[] fileNamesAndSig = getListOfFiles(dirName, true, userName, -1);
		System.out.println("Checking directory " + dirName + " (" + fixFileName(dirName) + ")\n");
		if (fileNamesAndSig != null) {
			System.out.println("The files and signatures of the directory " + dirName + " are:");
			for (String fileNameAndSig : fileNamesAndSig) {
				System.out.println(fileNameAndSig);
			}
			System.out.println("\nSuccessfully checked " + dirName);
		} else {
			System.out.println("Failed: no such directory " + dirName);
		}
	}

	static private boolean backupSignatue(String fileName, String userName, String signature) throws IOException {
		String fixedFileName = fixFileName(fileName);
		// System.out.println("Backupping signature of " + fileName + " (" + fixedFileName + ")");
		URL url = new URL(serverURL + fixedFileName + "?signature=" + signature);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("PUT");
		connection.addRequestProperty("Authorization", userName);
		connection.connect();

		int rc = connection.getResponseCode();
		if (rc == HttpURLConnection.HTTP_OK) {
			return true;
		} else if (rc == HttpURLConnection.HTTP_NOT_FOUND || rc == HttpURLConnection.HTTP_INTERNAL_ERROR || rc == HttpURLConnection.HTTP_BAD_REQUEST) {
			return false;
		}

		return false;
	}

	@SuppressWarnings("unused")
	static private byte[] getSignature(String fileName, String userName) throws IOException {
		String fixedFileName = fixFileName(fileName);

		System.out.println("Requesting signature of " + fileName + " (" + fixedFileName + ")");
		URL url = new URL(serverURL + fixedFileName + "?signature");
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("GET");
		connection.addRequestProperty("Authorization", userName);
		connection.connect();

		int rc = connection.getResponseCode();
		if (rc == HttpURLConnection.HTTP_OK) {
			System.out.println("Successfully received signature of " + fileName);
		} else if (rc == HttpURLConnection.HTTP_NOT_FOUND || rc == HttpURLConnection.HTTP_INTERNAL_ERROR || rc == HttpURLConnection.HTTP_BAD_REQUEST) {
			System.out.println("File " + fileName + " doesn't exist on server");
			return null;
		}

		String sigBase64 = new String(readInputStream(connection.getInputStream()));
		sigBase64 = sigBase64.replaceAll("\n", "");
		byte[] sigBin = Base64.decode(sigBase64, Base64.URL_SAFE);
		return sigBin;

	}

	static byte[] readInputStream(InputStream stream) throws IOException {
		final int chunkSize = 2048;
		byte[] buf = new byte[chunkSize];
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(chunkSize);
		int count;
		while ((count = stream.read(buf)) != -1)
			byteStream.write(buf, 0, count);
		return byteStream.toByteArray();
	}

	public static void deleteSnapshot(String userName, int snapshotNum) throws IOException {
		System.out.println("Deleting snapshot number " + snapshotNum);
		URL url = new URL(serverURL + "/?" + snapshotNum);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("DELETE");
		connection.addRequestProperty("Authorization", userName);
		connection.connect();

		int rc = connection.getResponseCode();
		if (rc == HttpURLConnection.HTTP_OK) {
			System.out.println("Successfully deleted snapshot number " + snapshotNum);
		} else if (rc == HttpURLConnection.HTTP_INTERNAL_ERROR) {
			System.out.println("Could not delete snapshot number " + snapshotNum);
		}
	}

	public static void createSnapshot(String userName, int snapshotNum) throws IOException {
		System.out.println("Creating snapshot number " + snapshotNum);
		URL url = new URL(serverURL + "/?" + snapshotNum);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("PUT");
		connection.addRequestProperty("Authorization", userName);
		connection.connect();

		int rc = connection.getResponseCode();
		if (rc == HttpURLConnection.HTTP_OK) {
			System.out.println("Successfully created snapshot number " + snapshotNum);
		} else if (rc == HttpURLConnection.HTTP_INTERNAL_ERROR) {
			System.out.println("Could not create snapshot number " + snapshotNum);
		}
	}
}
